#include "common.h"
#include "symtab.h"
#include "generate.h"
#include "gen_c.h"
#include "map.h"
#include "globals.h"
#include <time.h>

/* include big char array named 'Template' */
#include "gen_ctmp.c"


#define HEXFLAG (0x01)
#define DECFLAG (0x00)
#define TWOFLAG (0x10)
#define ONEFLAG (0x00)
#define HEX16   (HEXFLAG|TWOFLAG)
#define HEX8    (HEXFLAG|ONEFLAG)
#define DEC16   (DECFLAG|TWOFLAG)
#define DEC8    (DECFLAG|ONEFLAG)

void    VersionInsert(FILE* Output)
    {
    fprintf(Output, "0.0");
    }

static
void    FilenameInsert(FILE* Output)
    {
    fprintf(Output, "%s", Globals.InputFile);
    }

static
void    TimestampInsert(FILE* Output)
    {
    time_t  Now;

    Now     = time(NULL);

    fprintf(Output, "%s", asctime(localtime(&Now)));
    }

#if 0

void RuleJumpsInsert(FILE* Output, TParseTables* Tables)
    {
    int iRule;
    
    fprintf(Output, "   ");
    for(iRule = 0; iRule < Tables->RuleOffsets.Size; ++iRule)
        {
        if(iRule && !(iRule%10))
            fprintf(Output, "\n    ");
        else
            fprintf(Output, " ");
        fprintf(Output, "%5d,", Tables->RuleOffsets.v[iRule]);
        }
    fprintf(Output, "\n");
    }
void SelectJumpsInsert(FILE* Output, TParseTables* Tables)
    {
    int iSelect;
    
    fprintf(Output, "   ");
    for(iSelect = 0; iSelect < Tables->SelectOffsets.Size; ++iSelect)
        {
        if(iSelect && !(iSelect%10))
            fprintf(Output, "\n    ");
        else
            fprintf(Output, " ");
        fprintf(Output, "%5d,", Tables->SelectOffsets.v[iSelect]);
        }
    fprintf(Output, "\n");
    }

void    TranTypeDefInsert(FILE* Output, TParseTables* Tables)
    {
    fprintf(Output, "#if UCHAR_MAX >= %d\n",   Tables->MaxTransitionInt);
    fprintf(Output, "    typedef unsigned char   BLC_TRANS;\n");
    fprintf(Output, "#elif USHRT_MAX >= %d\n", Tables->MaxTransitionInt);
    fprintf(Output, "    typedef unsigned short  BLC_TRANS;\n");
    fprintf(Output, "#elif UINT_MAX >= %d\n",  Tables->MaxTransitionInt);
    fprintf(Output, "    typedef unsigned int    BLC_TRANS;\n");
    fprintf(Output, "#else\n");
    fprintf(Output, "    typedef unsigned long   BLC_TRANS;\n");
    fprintf(Output, "#endif\n");
    }
#endif

/* TokenDefsInsert() - emit #defines for named terminals.
 */
void TokenDefsInsert(FILE* Output)
    {
    int     iTerminal, NTerminals;

    NTerminals  = SymbolListCount(Globals.Terminals);
    for(iTerminal = 0; iTerminal < NTerminals; ++iTerminal)
        {
        TSymbol*    Terminal = SymbolListGet(Globals.Terminals, iTerminal);

        assert(Terminal != NULL);
        assert(SymbolGetBit(Terminal, SYM_TERMINAL));
        if(Terminal->Name.Text[0] != '\'')
            fprintf(Output, "#define %.*s %*.s(%d)\n",
                    Terminal->Name.TextLen,
                    Terminal->Name.Text,
                    MAX(32 - Terminal->Name.TextLen, 1),
                    " ",
                    Terminal->Value
                    );
        }
    }

void    TypeDefsInsert(FILE* Output, TParseTables* Tables)
    {
    fprintf(Output, "#if SCHAR_MAX >= %d && SCHAR_MIN <= %d\n",
            Tables->MaxTokenVal, Tables->MinTokenVal);
    fprintf(Output, "    typedef signed char BLC_TOKVAL;\n");
    fprintf(Output, "#elif SHRT_MAX >= %d && SHRT_MIN <= %d\n",
            Tables->MaxTokenVal, Tables->MinTokenVal);
    fprintf(Output, "    typedef short BLC_TOKVAL;\n");
    fprintf(Output, "#elif INT_MAX >= %d && INT_MIN <= %d\n",
            Tables->MaxTokenVal, Tables->MinTokenVal);
    fprintf(Output, "    typedef int BLC_TOKVAL;\n");
    fprintf(Output, "#else\n");
    fprintf(Output, "    typedef long BLC_TOKVAL;\n");
    fprintf(Output, "#endif\n");
//    fprintf(Output, "typedef int BLC_NEXTSTATE;\n");
    fprintf(Output, "typedef unsigned char BLC_OPCODE;\n");
    fprintf(Output, "typedef int BLC_VALTYPE;\n");
    }

#if 0
void    TransSizeInsert(FILE* Output, TParseTables* Tables)
    {
    fprintf(Output, "%d", Tables->TransitionsSize);
    }
void    ProdsSizeInsert(FILE* Output, TParseTables* Tables)
    {
    fprintf(Output, "%d", Tables->ProductionsSize);
    }
#endif

void    ValidTokensInsert(FILE* Output, TParseTables* Tables)
    {
    int     iToken;

    for(iToken = 0; iToken < Tables->LLColumns.Size; ++iToken)
        fprintf(Output, "%d, ", Tables->LLColumns.v[iToken]);

    }

void    NextStatesInsert(FILE* Output, TParseTables* Tables)
    {
    int     iState;

    for(iState = 0; iState < Tables->LLRows.Size; ++iState)
        fprintf(Output, "%d, ", Tables->LLRows.v[iState]);

    }

static
int     Load8(unsigned char* Opcodes, int* IP)
    {
    int     Result;
    
    Result  = Opcodes[*IP] & 0x0FF;
    ++*IP;
    assert(Result >= 0);

    return Result;
    }

static
int     Load16(unsigned char* Opcodes, int* IP)
    {
    int     Result;
    
    Dump("Load16(%d)\n", *IP);
    Result  = Opcodes[*IP] & 0x0FF;
    ++*IP;
    Result  = Result | ((Opcodes[*IP] & 0x0FF) << 8);
    ++*IP;
    assert(Result >= 0);

    Dump("Load16() returns %d\n", Result);
    return Result;
    }

/* TokenStr() - fetch string version of token from token value
 */
static
const char* TokenStr(int TokenVal)
    {
    const char* Result  = NULL;
    SymIt       Terminal;

    Terminal    = SymItNew(Globals.Terminals);
    while(SymbolIterate(&Terminal))
        if(Terminal.Symbol->Value == TokenVal)
            {
            Result = SymbolStr(Terminal.Symbol);
            break;
            }
    if(Result == NULL)
        {
        fprintf(stderr, "%d is not a valid token value!\n", TokenVal);
        assert(Result != NULL);
        }

    return Result;
    }

static
void    EmitAddress(FILE* Output, int IP, const char* Format, ...)
    {
    va_list ArgPtr;

    fprintf(Output, "/*0x%04X:        ", IP);
    va_start(ArgPtr, Format);
    vfprintf(Output, Format, ArgPtr);
    va_end(ArgPtr);
    fprintf(Output, "  */\n");
    }

static
void    Emit(FILE* Output, int Flag, int Value, const char*Format, ...)
    {
    size_t  Len;
    char*   Buffer;
    va_list ArgPtr;

    Dump("Emit()\n");
    Buffer  = malloc(1024*4);
    assert(Buffer != NULL);

    if(Flag&TWOFLAG)
        sprintf(Buffer, (Flag&HEXFLAG) ? "    0x%02X,0x%02X," : "    %u,%u,",
                Value & 0x0FF, (Value>>8)&0x0FF);
    else
        sprintf(Buffer, (Flag&HEXFLAG) ? "    0x%02X," : "    %u,",
                Value & 0x0FF );
    Len     = strlen(Buffer);
    if(Len < 16)
        Len     = 16 - Len;
    else
        Len     = 0;

    fprintf(Output, "%s%*.*s", Buffer, Len, Len, "");

    if(Format)
        {
        va_start(ArgPtr, Format);
        vsprintf(Buffer, Format, ArgPtr);
        va_end(ArgPtr);

        fprintf(Output, "/* %s */\n", Buffer);
        }
    else
        fprintf(Output, "\n");

    free(Buffer);
    Dump("Emit() returns\n");
    }

static
int EmitSelectBody(FILE* Output, unsigned char* Opcodes, int IP)
    {
    int     Count;
    int     OldIP;

    /* first emit the token ranges */
    Count   = Load8(Opcodes, &IP);
    OldIP   = IP;
    while(Count > 0)
        {
        int     Start, End;
        int     Addr;
    
        Emit(Output, DEC8, Count, "%d token ranges in this group", Count);
        Addr    = Load16(Opcodes, &IP);
        Emit(Output, HEX16, Addr, "0x%04X: address of rule", Addr);
        while(Count-- > 0)
            {
            Start   = Load8(Opcodes, &IP);
            End     = Load8(Opcodes, &IP);
            Emit(Output, DEC16, Start|(End<<8), "%s - %s", TokenStr(Start), TokenStr(End));
            }
        Count   = Load8(Opcodes, &IP);
        }

    Emit(Output, DEC8, 0, (IP > OldIP) ? "EOL for token ranges" : "EOL (no token ranges)");

    /* then emit the token groups */
    Count   = Load8(Opcodes, &IP);
    OldIP   = IP;
    while(Count > 0)
        {
        int     TokenId;
        int     Addr;
    
        Emit(Output, DEC8, Count, "%d tokens for this rule", Count);
        Addr    = Load16(Opcodes, &IP);
        Emit(Output, HEX16, Addr, "0x%04X: address of rule", Addr);
        while(Count-- > 0)
            {
            TokenId = Load8(Opcodes, &IP);
            Emit(Output, DEC8, TokenId, "%s", TokenStr(TokenId));
            }
        Count   = Load8(Opcodes, &IP);
        }

    Emit(Output, DEC8, 0, (IP > OldIP) ? "EOL for token lists" : "EOL (no token lists)");

    return IP;
    }

/* ActionNote() - create an abbreviated comment about an action.
 *
 * We want to show part of the action in a comment.
 */
#define ACTNOTESIZE (64)
static
const char* ActionNote(TAction* Action)
    {
    static char Note[ACTNOTESIZE+1];
    char*       Rover;
    const char* Input;
    const char* Start;
    size_t      Len;

    Rover   = Note;
    *Rover  = '\0';
    
    Input   = Start = Action->Action.Text;
    Len     = Action->Action.TextLen;
    while((Rover - Note) < ACTNOTESIZE)
        {
        switch(*Input)
            {
            case '\r' :
            case '\n' :
            case '\t' :
                *Rover++ = ' '; ++Input;
                while(isspace(*Input) && ((unsigned)(Input - Start)) < Len)
                    ++Input;
                break;
            case '/' :
                if(Input > Start && Input[-1] == '*')
                    {
                    *Rover++ = ')'; 
                    ++Input;
                    }
                else if(Input[1] != '*')
                    *Rover++ = *Input++;
                else
                    {
                    *Rover++ = '('; 
                    ++Input;
                    }
                break;
            default:
                *Rover++ = *Input++;
            }
        if((unsigned)(Input - Start) > Len)
            break;
        }

    *Rover = '\0';
    return Note;
    }

/* OpcodesInsert() - insert the entire opcode table.
 *
 * We try to emit this in a commented, understandable way.
 */
static
void    OpcodesInsert(FILE* Output, TParseTables* Tables)
    {
    TSymbol*    Symbol;
    BLC_OPCODE* Opcodes;
    int         IP, iSelect, NSelect, iRule, Reduced, iSymbol;
    int         NSymbols;
    int         TermSymTabSize, NonTermSymTabSize, EntryTabSize, SelectTabSize, RuleTabSize;
    int         OpcodesOffset, SelectOffset;

    Dump("OpcodesInsert()\n");
    Opcodes = Tables->Opcodes;
    assert(Opcodes != NULL);

    IP          = 0;

    /* Emit header information */
    TermSymTabSize      = Load16(Opcodes, &IP);
    Emit(Output, DEC16, TermSymTabSize, "[%4d] terminal symbol table size", TermSymTabSize);
    NonTermSymTabSize   = Load16(Opcodes, &IP);
    Emit(Output, DEC16, NonTermSymTabSize, "[%4d] nonterminal symbol table size", NonTermSymTabSize);
    EntryTabSize        = Load16(Opcodes, &IP);
    Emit(Output, DEC16, EntryTabSize, "[%4d] entry table size", EntryTabSize);
    SelectTabSize       = Load16(Opcodes, &IP);
    Emit(Output, DEC16, SelectTabSize, "[%4d] selection table size", SelectTabSize);
    RuleTabSize         = Load16(Opcodes, &IP);
    Emit(Output, DEC16, RuleTabSize, "[%4d] rule opcode table size", RuleTabSize);

    NSelect = SymbolListCount(Tables->LLNonTerms);
//    assert(NSelect == Load16(Opcodes, &IP));
//    Emit(Output, DEC16, NSelect, "%d LL nonterminals", NSelect);
//    Start   = Load16(Opcodes, &IP);
//    Emit(Output, HEX16, Start, "0x%04X: address of entry point", Start);
    /* emit terminal symbol table */
    fprintf(Output, "/* Terminal symbol table */\n");
    while(IP < 10 + TermSymTabSize)
        {
        Emit(Output, DEC8, Opcodes[IP], "[%4d] '%s'", Opcodes[IP], Opcodes+IP+1);
        ++IP;
        for(;;)
            {
            int     Col;

            fprintf(Output, "    ");
            for(Col = 0; Col < 4; ++Col)
                {
                if(isalnum(Opcodes[IP]))
                    fprintf(Output, "'%c', ", Opcodes[IP]);
                else
                    fprintf(Output, "%2d, ", Opcodes[IP]);
                if(Opcodes[IP++] == 0)
                    break;
                }
            fprintf(Output, "\n");
            if(Opcodes[IP-1] == 0)
                break;
            }
        if(Opcodes[IP] == 0)
            break;
        }
    assert(Opcodes[IP] == 0);
    Emit(Output, DEC8, Opcodes[IP], "sentinel byte");
    ++IP;

    fprintf(Output, "/* Nonterminal symbol table */\n");
    iSymbol = 0;
    while(IP < 10 + TermSymTabSize + NonTermSymTabSize)
        {
        fprintf(Output, "/* [%d] %s */\n", iSymbol++, Opcodes+IP);
        for(;;)
            {
            int     Col;

            fprintf(Output, "    ");
            for(Col = 0; Col < 4; ++Col)
                {
                if(isalnum(Opcodes[IP]))
                    fprintf(Output, "'%c', ", Opcodes[IP]);
                else
                    fprintf(Output, "%2d, ", Opcodes[IP]);
                if(Opcodes[IP++] == 0)
                    break;
                }
            fprintf(Output, "\n");
            if(Opcodes[IP-1] == 0)
                break;
            }
        if(Opcodes[IP] == 0)
            break;
        }
    assert(Opcodes[IP] == 0);
    Emit(Output, DEC8, Opcodes[IP], "sentinel byte");
    ++IP;

    fprintf(Output, "/* entry table */\n");
    Symbol  = SymbolStart();
    for(iRule = 0; iRule < RuleCount(Symbol); ++iRule)
        {
        Emit(Output, HEX16, Opcodes[IP] | (Opcodes[IP+1]<<8), "%s",
             SymbolStr(SymbolListGet(Symbol->Rules[iRule]->Symbols, 0)));
        IP  += 2;
        }

    while(IP < 10 + TermSymTabSize + NonTermSymTabSize + EntryTabSize)
        {
        }

    fprintf(Output, "/* selection table */\n");
    /* emit LLSELECT bodies */
    SelectOffset    = IP;
    for(iSelect=0; iSelect < NSelect; ++iSelect)
        {
        TSymbol*    NonTerm = SymbolListGet(Tables->LLNonTerms, iSelect);

        assert(NonTerm != NULL);
        fprintf(Output, "/*0x%04X:%9s LLSELECT body for '%s' */\n", IP-SelectOffset, "", SymbolStr(NonTerm));
        IP  = EmitSelectBody(Output, Opcodes, IP);
        }

    OpcodesOffset   = IP;
    /* emit actual opcodes */
    fprintf(Output, "\n/*0x%04X:%9s Begin opcodes */\n", IP, "");
    /* generate opcodes for each unique rule */
    for(iRule = 0; iRule < Tables->NRules; ++iRule)
        {
        unsigned    Address;
        int         iProdItem, iSymbol;
        TRule*      Rule        = Tables->Rules[iRule];
        SYMBOLS     Symbols     = Tables->RuleSymbols[iRule];

        Reduced     = FALSE; /* have not performed a reduction for this rule yet */
        NSymbols    = SymbolListCount(Rule->Symbols);
fprintf(Output, "/*[0x%04X] ", IP - OpcodesOffset);
        for(iSymbol=0; (Symbol=SymbolListGet(Symbols, iSymbol)) != NULL; ++iSymbol)
            fprintf(Output, "%s%s", iSymbol?" | ":"", SymbolStr(Symbol));
fprintf(Output, " -> ");
SymbolListDump(Output, Rule->Symbols, " ");
fprintf(Output, " */\n");

        for(iProdItem = 0; iProdItem < NSymbols; ++iProdItem)
            {
            TSymbol*    Symbol = SymbolListGet(Rule->Symbols, iProdItem);

            if(SymbolGetBit(Symbol, SYM_TERMINAL))
                {
                assert(Opcodes[IP] == BLCOP_MATCH);
                Emit(Output, HEX8, Opcodes[IP++], "MATCH %s", SymbolStr(Symbol));
                Emit(Output, DEC8, Opcodes[IP++], NULL);

                if(Symbol == SymbolFind(EOFToken))
                    {
                    assert(Opcodes[IP] == BLCOP_HALT);
                    Emit(Output, HEX8, Opcodes[IP++], "HALT");
                    Reduced = TRUE;
                    }
//                IP      = Store8(Opcodes, IP, BLCOP_MATCH);
//                IP      = Store8(Opcodes, IP, Symbol->Value);
                }
            else if(SymbolIsAction(Symbol))
                {
                int  FinalAction;

                assert(Rule->RuleId < 255);
                assert(iProdItem < 255);

                FinalAction = FALSE;
                if(iProdItem == NSymbols-1)
                    FinalAction = TRUE;
                else if(Rule->TailRecursive && iProdItem == NSymbols-2)
                    FinalAction = TRUE;

                if(FinalAction) /* if it's the final action */
                    {
                    assert(Opcodes[IP] == BLCOP_ACTRED);
                    Emit(Output, HEX8, Opcodes[IP++], "BLCOP_ACTRED %s", ActionNote(Symbol->Action));
                    Emit(Output, HEX8, Opcodes[IP++], "    rule#");
                    Emit(Output, HEX8, Opcodes[IP++], "    <argcount>");
                    Reduced = TRUE;
                    }
                else
                    {
                    assert(Opcodes[IP] == BLCOP_ACTION8);
                    Emit(Output, HEX8, Opcodes[IP++], "BLCOP_ACTION8 %s", ActionNote(Symbol->Action));
                    Emit(Output, HEX8, Opcodes[IP++], "    rule#");
                    Emit(Output, HEX8, Opcodes[IP++], "    <argcount>");
                    }
                assert(Rule->RuleId < 255);
                assert(iProdItem < 255);
                }
            else if(SymbolListContains(Tables->LLNonTerms, Symbol))    /* non-terminal */
                {
                int iSymbol = SymbolListContains(Tables->LLNonTerms, Symbol);

                if(RuleCount(Symbol) > 1)
                    {
                    if(Symbol->Name.Text[0] == '`') /* if a tail recursive rule... */
                        {
                        Emit(Output, HEX8,  Opcodes[IP++], "BLCOP_TAILSELECT %s", SymbolStr(Symbol));
                        Emit(Output, HEX8,  Opcodes[IP++], NULL);
                        Reduced = TRUE;
                        }
                    else
                        Emit(Output, HEX8,  Opcodes[IP++], "BLCOP_LLSELECT %s", SymbolStr(Symbol));

                    Address     = Opcodes[IP++];
                    Address    |= (Opcodes[IP++] << 8);
                    Emit(Output, HEX16, Address, NULL);
//                    Emit(Output, HEX16, Opcodes[IP++] | (Opcodes[IP++]<<8), NULL);
//                    IP      = Store8(Opcodes, IP, BLCOP_LLSELECT);
//                    IP      = Store16(Opcodes, IP, Symbol->SelectOffset);
                    }
                /* else, only 1 rule to choose from, so just transfer control to that rule! */
                else
                    {
//                    int     iRule = AddRule(Tables, Symbol->Rules[0]);
                    
                    Emit(Output, HEX8,  Opcodes[IP++], "BLCOP_CALL %s", SymbolStr(Symbol));
                    Address     = Opcodes[IP++];
                    Address    |= (Opcodes[IP++] << 8);
                    Emit(Output, HEX16, Address, NULL);
//                    IP      = Store8(Opcodes, IP, BLCOP_CALL);
//                    IP      = MarkPatch(IP, iRule);
                    }

                assert(iSymbol>0);
                }
            else // else, it's an operator trigger
                fprintf(stderr, "watch out: we don't handle LR(0) yet!\n");
            }
        if(!Reduced)
            {
            Emit(Output, HEX8,  Opcodes[IP], "BLCOP_REDUCE %d", Opcodes[IP+1]);
            ++IP;
            Emit(Output, DEC8,  Opcodes[IP++], "");
//            IP  = Store8(Opcodes, IP, BLCOP_REDUCE);
//            IP  = Store8(Opcodes, IP, NSymbols);
            }
        }
#if 0



    while(IP < Tables->NOpcodes)
        IP  = EmitOpcode(Output, Opcodes, IP);
#endif
    Dump("OpcodesInsert() returns\n");
    }

#if 0

void    TransitionsInsert(FILE* Output, TParseTables* Tables)
    {
    TSymbol*    NonTerm;
    int         NNonTerms, NTokens;
    int         iToken, iNonTerm, iTransition;

    NNonTerms   = SymbolListCount(Globals.NonTerms);

    iTransition = 1;
    for(iNonTerm = 0; iNonTerm < NNonTerms; ++iNonTerm)
        {
        NonTerm     = SymbolListGet(Globals.NonTerms, iNonTerm);
        if(NonTerm->OperatorTrigger || NonTerm == SymbolStart())
            continue;
        fprintf(Output, "/*[%3d] %.*s %*.s*/\n", iTransition,
                NonTerm->Name.TextLen, NonTerm->Name.Text,
                MAX(1,32-NonTerm->Name.TextLen), "");
        fprintf(Output, "    ");

        NTokens     = SymbolListCount(NonTerm->InputTokens);
        for(iToken = 0; iToken < NTokens; ++iToken)
            {
            TSymbol*    Input;

            /* get next input token that produces a transition for this non-terminal */
            Input   = SymbolListGet(NonTerm->InputTokens, iToken);
            assert(Input != NULL);

            assert(Tables->Transitions[iTransition] == Input->Value);
            if(Input->Name.Text[0] == '\'')
                {
                if(Input->Name.TextLen == 3 && isgraph(Input->Name.Text[1]))
                    fprintf(Output, "%.*s,", Input->Name.TextLen, Input->Name.Text);
                else
                    fprintf(Output, "%d,", Tables->Transitions[iTransition]);
                }
            else
                fprintf(Output, "%.*s,", Input->Name.TextLen, Input->Name.Text);
            ++iTransition;
            fprintf(Output, "%d, ", Tables->Transitions[iTransition++]);
            }
        assert(Tables->Transitions[iTransition] == EolSymbol->Value);
        fprintf(Output, "%.*s,", EolSymbol->Name.TextLen, EolSymbol->Name.Text);
        ++iTransition;

        fprintf(Output, "\n");
        }
    }
#endif

#if 0
void    ProductionsInsert(FILE* Output, TParseTables* Tables)
    {
    TSymbol*    NonTerm;
    int         NNonTerms, NSymbols;
    int         iRule, iNonTerm, iProduction;

    NNonTerms   = SymbolListCount(Globals.NonTerms);

    iProduction = 0;
    for(iNonTerm = 0; iNonTerm < NNonTerms; ++iNonTerm)
        {
        NonTerm     = SymbolListGet(Globals.NonTerms, iNonTerm);

        /* if it belongs to operator precedence grammar */
        if(NonTerm->OperatorTrigger || NonTerm == SymbolStart())
            continue;   /* then skip it */
        
        /* for each non-terminal production */
        for(iRule = 0; iRule < NonTerm->NRules; ++iRule)
            {
            int     TokenOrOffset, iSymbol;
            TRule*  Rule;
            
            assert(NonTerm->Rules != NULL);
            Rule    = NonTerm->Rules[iRule];
            assert(Rule != NULL);
            NSymbols    = SymbolListCount(Rule->Symbols);

            fprintf(Output, "/*[%3d]%.*s%*.s: ", iProduction,
                    NonTerm->Name.TextLen, NonTerm->Name.Text,
                    MAX(1,12-NonTerm->Name.TextLen), "");

            if(NSymbols == 0)
                fprintf(Output, "<epsilon>");
            else
                SymbolListDump(Output, Rule->Symbols, " ");
            fprintf(Output, " */\n");
            assert(iProduction + NSymbols + 2 <= Tables->ProductionsSize);
            fprintf(Output, "       ");
            /* for each symbol on RHS of production */
            for(iSymbol = 0; iSymbol < NSymbols; ++iSymbol)
                {
                TSymbol*    Symbol = SymbolListGet(Rule->Symbols, iSymbol);

                assert(Symbol != NULL);
                if(Symbol->Type == TERMINAL)
                    TokenOrOffset   = Symbol->Value;
                else
                    TokenOrOffset   = Symbol->TransitionOffset;
                fprintf(Output, "%d,", Tables->Productions[iProduction++]);
                
                }
            /* emit end-of-list separator */
            fprintf(Output, " %.*s,", EolSymbol->Name.TextLen, EolSymbol->Name.Text);
            ++iProduction;
            /* emit stack size  */
            fprintf(Output, " %d, ", Tables->Productions[iProduction++]);
            /* emit action# associated with this production   */
            if(Tables->Productions[iProduction])
                fprintf(Output, " %d", Tables->Productions[iProduction]);
            else
                fprintf(Output, " BLC_NOACTION");
            ++iProduction;
            if((iRule+1) == NonTerm->NRules && (iNonTerm+1) == NNonTerms)
                ;
            else
                fprintf(Output, ",\n");
            }
        }
    }
#endif
#if 0
void    ProductionsInsert(FILE* Output, TParseTables* Tables)
    {
    TSymbol*    NonTerm;
    int         NNonTerms, NSymbols;
    int         iRule, iNonTerm, iProduction;
    int         ByteOffset;
    MAP         SymMap;

    SymMap      = MapCreate(NULL);
    NNonTerms   = SymbolListCount(Globals.NonTerms);

    ByteOffset  = 0;
    iProduction = 0;
    for(iNonTerm = 0; iNonTerm < NNonTerms; ++iNonTerm)
        {
        NonTerm     = SymbolListGet(Globals.NonTerms, iNonTerm);

        /* if it belongs to operator precedence grammar */
        if(NonTerm->OperatorTrigger || NonTerm == SymbolStart())
            continue;   /* then skip it */
        
        /* for each non-terminal production */
        for(iRule = 0; iRule < NonTerm->NRules; ++iRule)
            {
            int     TokenOrOffset, iSymbol;
            TRule*  Rule;
            
            assert(NonTerm->Rules != NULL);
            Rule    = NonTerm->Rules[iRule];
            assert(Rule != NULL);
            NSymbols    = SymbolListCount(Rule->Symbols);

            fprintf(Output, "/*[%3d]%.*s%*.s: ", iProduction,
                    NonTerm->Name.TextLen, NonTerm->Name.Text,
                    MAX(1,12-NonTerm->Name.TextLen), "");

            if(NSymbols == 0)
                fprintf(Output, "<epsilon>");
            else
                SymbolListDump(Output, Rule->Symbols, " ");
            fprintf(Output, " */\n");
            assert(iProduction + NSymbols + 2 <= Tables->ProductionsSize);
            fprintf(Output, "       ");
            /* for each symbol on RHS of production */
            for(iSymbol = 0; iSymbol < NSymbols; ++iSymbol)
                {
                TSymbol*    Symbol = SymbolListGet(Rule->Symbols, iSymbol);

                assert(Symbol != NULL);
                if(Symbol->Type == TERMINAL)
                    fprintf(Output, "BLCOP_MATCH, %d,\t/* MATCH %s */\n",
                            Symbol->Value, Symbol->Name.Text, 4);
                else
                    TokenOrOffset   = Symbol->TransitionOffset;
                fprintf(Output, "%d,", Tables->Productions[iProduction++]);
                
                }
            /* emit end-of-list separator */
            fprintf(Output, " %.*s,", EolSymbol->Name.TextLen, EolSymbol->Name.Text);
            ++iProduction;
            /* emit stack size  */
            fprintf(Output, " %d, ", Tables->Productions[iProduction++]);
            /* emit action# associated with this production   */
            if(Tables->Productions[iProduction])
                fprintf(Output, " %d", Tables->Productions[iProduction]);
            else
                fprintf(Output, " BLC_NOACTION");
            ++iProduction;
            if((iRule+1) == NonTerm->NRules && (iNonTerm+1) == NNonTerms)
                ;
            else
                fprintf(Output, ",\n");
            }
        }
    }
#endif

/* ProgramInsert() - insert the user program code.
 *
 * This is whatever follows the second '%%' in the source.
 */
static
void    ProgramInsert(FILE* Output, TParseTables* Tables)
    {
    ARGUNUSED(Tables);
    if(Globals.ProgramSection.Type == TK_PROGRAM)
        {
        fputs(Globals.ProgramSection.Text, Output);
        }
    }


static
void    ActionsInsert(FILE* Output)
    {
    int     iAction;
    SymIt   NonTerm = SymItNew(Globals.Actions);

    for(iAction=0; SymbolIterate(&NonTerm); ++iAction)
        {
        TAction*    Action = NonTerm.Symbol->Action;

        if(Action)
            {
            const char* Rover;
            const char* End;
printf("Action %d has length %d\n", Action->Number, Action->Action.TextLen);
            fprintf(Output, "    case %d :\n        {\n", Action->Number);
            Rover = Action->Action.Text;
            End   = Rover + Action->Action.TextLen;
            for(; Rover < End; ++Rover)
                {
                if(*Rover == '$')
                    {
                    if(*++Rover == '$')
                        fprintf(Output, "(State.ReturnValue)");
                    else if(isdigit(*Rover))
                        {
                        int   Offset = 0;
                        while(isdigit(*Rover))
                            {
                            Offset = Offset * 10 + (*Rover - '0');
                            ++Rover;
                            }
                        --Rover;    /* correct overshoot */
                        fprintf(Output, "/*Offset=%d,ArgCount=%d*/(State.VSP[%d])", Offset, Action->ArgCount, (Offset-Action->ArgCount)-1);
                        }
                    else
                        fprintf(Output, "$%c", *Rover);
                    }
                else
                    fprintf(Output, "%c", *Rover);
                }
//            fprintf(Output, "%.*s", Action->Action.TextLen, Action->Action.Text);
            fprintf(Output, "\n        }\n");
            fprintf(Output, "break;\n");
            }
        }
    }


static
int     MatchAlias(const char* Input, const char* Name, size_t Len)
    {
    size_t  NameLen = strlen(Name);

    return (Len == NameLen) && !strncmp(Input, Name, NameLen);
    }

const char* DoInsert(FILE* Output, TParseTables* Tables, const char* Rover)
    {
    size_t      NameLen;
    const char* Name;

    Name    = ++Rover;
    while(*Rover != '@')
        {
        assert(isalpha(*Rover));
        ++Rover;
        }
    NameLen = Rover - Name;
    assert(NameLen > 0);

    Dump("DoInsert(%.*s)\n", NameLen, Name);

    if(!strncmp(Name, "VERSION", strlen("VERSION")))
        VersionInsert(Output);

    else if(MatchAlias(Name, "ACTIONS", NameLen))
        ActionsInsert(Output);
    else if(MatchAlias(Name, "FILENAME", NameLen))
        FilenameInsert(Output);
    else if(MatchAlias(Name, "TIMESTAMP", NameLen))
        TimestampInsert(Output);
    else if(MatchAlias(Name, "TYPEDEFS",NameLen))
        TypeDefsInsert(Output, Tables);
    else if(MatchAlias(Name, "TOKENDEFS",NameLen))
        TokenDefsInsert(Output);
    else if(!strncmp(Name, "VALIDTOKENS", strlen("VALIDTOKENS")))
        ValidTokensInsert(Output, Tables);
    else if(!strncmp(Name, "NEXTSTATESIZE", strlen("NEXTSTATESIZE")))
        fprintf(Output, "%d", Tables->LLRows.Size);
#if 0
    else if(!strncmp(Name, "RULEJUMPSIZE", strlen("RULEJUMPSIZE")))
        fprintf(Output, "%d", Tables->RuleOffsets.Size);
    else if(!strncmp(Name, "RULEJUMPS", strlen("RULEJUMPS")))
        RuleJumpsInsert(Output, Tables);
    else if(!strncmp(Name, "SELECTJUMPSIZE", strlen("SELECTJUMPSIZE")))
        fprintf(Output, "%d", Tables->SelectOffsets.Size);
    else if(!strncmp(Name, "SELECTJUMPS", strlen("SELECTJUMPS")))
        SelectJumpsInsert(Output, Tables);
    else if(!strncmp(Name, "NEXTSTATES", strlen("NEXTSTATES")))
        NextStatesInsert(Output, Tables);
#endif
    else if(!strncmp(Name, "OPCODESIZE", strlen("OPCODESIZE")))
        fprintf(Output, "%d /* 0x%04X */", Tables->NOpcodes, Tables->NOpcodes);
    else if(!strncmp(Name, "OPCODES", strlen("OPCODES")))
        OpcodesInsert(Output, Tables);
    else if(!strncmp(Name, "PROGRAM", strlen("PROGRAM")))
        ProgramInsert(Output, Tables);
    else if(!strncmp(Name, "VALTOKSIZE", strlen("VALTOKSIZE")))
        fprintf(Output, "%d", Tables->LLColumns.Size);
#if 0
    else if(!strncmp(Name, "TRANSSIZE", strlen("TRANSSIZE")))
        TransSizeInsert(Output, Tables);
    else if(!strncmp(Name, "PRODSSIZE", strlen("PRODSSIZE")))
        ProdsSizeInsert(Output, Tables);
    else if(!strncmp(Name, "TRANSITIONS", strlen("TRANSITIONS")))
        TransitionsInsert(Output, Tables);
    else if(!strncmp(Name, "PRODUCTIONS", strlen("PRODUCTIONS")))
        ProductionsInsert(Output, Tables);
#endif
//    else if(!strncmp(Name, "OPCODETYPEDEF", strlen("OPCODETYPEDEF")))
//        fprintf(Output, "typedef int OPCODETYPEDEF;\n");
    else
        ErrorExit(ERROR_BAD_NAME_IN_TEMPLATE, "Can't happen. Bad name in template: '%.*s'\n",
              NameLen, Name);

    return Rover + 1;
    }

int     GenerateC(FILE* Output, TParseTables* Tables)
    {
    const char* Rover = Template;

    Dump("GenerateC() begins (opcode table = %d bytes)\n", Tables->NOpcodes);
    while(*Rover)
        if(*Rover == '@')
            Rover   = DoInsert(Output, Tables, Rover);
        else
            fputc(*Rover++, Output);

    Dump("GenerateC() returns\n");
    return TRUE;
    }

